package com.framework.core.annotation;

import com.alibaba.fastjson.JSON;
import com.framework.core.constant.Globals;
import com.framework.core.domain.SessionVo;
import com.framework.core.exception.BusinessException;
import com.framework.core.util.ContextHolderUtils;
import com.framework.core.util.StringUtil;
import com.framework.web.sys.domain.Log;
import com.framework.web.sys.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.StringTokenizer;

/**
 * 切点类
 */
@Aspect
@Component
public class SystemLogAspect {

    //注入Service用于把日志保存数据库
    @Autowired
    private LogService logService;

    //本地异常日志记录对象
    private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);

    //Service层切点
    @Pointcut("@annotation(com.framework.core.annotation.SystemServiceLog)")
    public void serviceAspect() {
    }

    //Controller层切点
    @Pointcut("@annotation(com.framework.core.annotation.SystemControllerLog)")
    public void controllerAspect() {
    }

    @Pointcut("execution(* com.framework.web.*.controller..*(..))")
    private void anyMethod() {
    }

    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @Before("controllerAspect()")
    public void doBefore(final JoinPoint joinPoint) {

        HttpServletRequest request = ContextHolderUtils.getRequest();
        final SessionVo sessionVo = ContextHolderUtils.getSessionVo();
        final String ip = request.getRemoteAddr();
        final String Agent = request.getHeader("User-Agent");
        StringTokenizer st = new StringTokenizer(Agent, ";");
        st.nextToken();
        //得到用户的浏览器名
        final String userBrowser = st.nextToken();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //获取用户请求方法的参数并序列化为JSON格式字符串
                    String params = "";
                    try {
                        /*Object[] objects = joinPoint.getArgs();
                        logger.debug(objects.toString());*/
                        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                                if (joinPoint.getArgs()[i] instanceof HttpServletRequest ||joinPoint.getArgs()[i] instanceof HttpServletResponse) {
                                } else {
                                    params += JSON.toJSONString(joinPoint.getArgs()[i]);
                                }
                            }
                        }
                    } catch (Exception e2) {
                        logger.info(e2.getMessage());
                    }
                    //*========控制台输出=========*//
                    logger.info("=====前置通知开始1=====");
                    logger.info("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
                    logger.info("请求参数:" + params);
                    logger.info("方法描述:" + getControllerMethodDescription(joinPoint));
                    logger.info("请求人:" + sessionVo.getRealName());
                    logger.info("请求IP:" + ip);
                    Log log = new Log();
                    log.setUserId(sessionVo.getUserId());
                    log.setUserName(sessionVo.getUserName());
                    log.setRealName(sessionVo.getRealName());
                    log.setLogType(Globals.LOG_TYPE_OPERATE);//日志类型
                    log.setIpAddress(ip);//ip地址
                    log.setBrowserType(userBrowser);
                    log.setBrowserInfo(Agent);
                    log.setRequestMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
                    log.setRequestParams(params);
                    log.setOperationInfo(getControllerMethodDescription(joinPoint));
                    log.setOperateTime(new Date());
                    logService.save(log);
                    logger.info("=====前置通知结束1=====");
                } catch (Exception e) {
                    //记录本地异常日志
                    logger.error("==前置通知异常==");
                    logger.error("异常信息:{}", e.getMessage());
                }
            }
        }).start();
    }

    /**
     * 异常通知 用于拦截Controller层记录异常日志
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "anyMethod()", throwing = "e")
    public void doAfterThrowing(final JoinPoint joinPoint, final Throwable e) {
        HttpServletRequest request = ContextHolderUtils.getRequest();
        final SessionVo sessionVo = ContextHolderUtils.getSessionVo();
        final String ip = request.getRemoteAddr();
        final String Agent = request.getHeader("User-Agent");
        StringTokenizer st = new StringTokenizer(Agent, ";");
        st.nextToken();
        //得到用户的浏览器名
        final String userBrowser = st.nextToken();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //获取用户请求方法的参数并序列化为JSON格式字符串
                    String params = "";
                    try {
                        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                                if (joinPoint.getArgs()[i] instanceof HttpServletRequest ||joinPoint.getArgs()[i] instanceof HttpServletResponse) {
                                } else {
                                    params += JSON.toJSONString(joinPoint.getArgs()[i]);
                                }
                            }
                        }
                    } catch (Exception e2) {
                        logger.info(e2.getMessage());
                    }
                    //*========控制台输出=========*//
                    logger.info("=====前置异常通知开始=====");
                    logger.info("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
                    logger.info("请求参数:" + params);
                    logger.info("方法描述:" + getControllerMethodDescription(joinPoint));
                    logger.info("请求人:" + sessionVo.getRealName());
                    logger.info("请求IP:" + ip);
                    Log log = new Log();
                    log.setUserId(sessionVo.getUserId());
                    log.setUserName(sessionVo.getUserName());
                    log.setRealName(sessionVo.getRealName());
                    log.setIpAddress(ip);//ip地址
                    log.setLogType(Globals.LOG_TYPE_EXCEPTION);//日志类型
                    log.setBrowserType(userBrowser);
                    log.setBrowserInfo(Agent);
                    log.setIpAddress(ip);//ip地址
                    log.setRequestMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
                    log.setRequestParams(params);
                    log.setOperationInfo(e.getMessage());
                    log.setOperateTime(new Date());
                    logService.save(log);
                    logger.info("=====前置异常通知结束=====");
                } catch (Exception e) {
                    //记录本地异常日志
                    logger.error("==前置通知异常==");
                    logger.error("异常信息:{}", e.getMessage());
                }
            }
        }).start();
    }


    /**
     * 获取注解中对方法的描述信息 用于service层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public static String getServiceMethodDescription(JoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(SystemServiceLog.class).description();
                    break;
                }
            }
        }
        return description;
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public static String getControllerMethodDescription(JoinPoint joinPoint) {
        try {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String description = "";
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        description = method.getAnnotation(SystemControllerLog.class).description();
                        break;
                    }
                }
            }
            return description;
        } catch (Exception e) {
        }
        return "";
    }

    /**
     * 异常消息
     *
     * @param e
     * @return
     */
    private static String getExMessageString(Throwable e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        e.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }
}